﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Ionic.Zip;
using ResxHelper.Logic.Actions;
using ResxHelper.Logic.Readers;
using ResxHelper.Object;

namespace ResxHelper.Logic.Converter
{
    public abstract class BulkConverterBase : IBulkConvert
    {
        protected DirectoryInfo WorkingDirectory;
        protected SaveFileType SaveFileType;

        /// <summary>
        /// Returns final path to the final resource file by parsing the name.
        /// </summary>
        /// <param name="fileName">Excel file name without the extension.</param>
        /// <returns></returns>
        protected abstract string GetFilePathFromFileName(string fileName);

        /// <summary>
        /// Creates a file name that will be used to rename the exported file. The file name will be formatted using a convention which will aide in the recreation of the file structure upon import.
        /// </summary>
        /// <param name="resourceFile">An instance of the resource file being named.</param>
        /// <returns></returns>
        protected abstract string GetExportFileName(ResourceFile resourceFile);

        /// <summary>
        /// Traverses the directory structure in attempts to find some resource files
        /// </summary>
        /// <param name="tempPath">Path to directory containing all folders to search.</param>
        /// <param name="resourceDirectoryName">A keyword that will trigger recursion to end and search the directory for resource files</param>
        /// <returns></returns>
        protected IEnumerable<ResourceFile> ReadDirectories(string tempPath, string resourceDirectoryName)
        {
            var resourceFiles = new List<ResourceFile>();

            // recurse directories to find resource files
            foreach (var directory in Directory.GetDirectories(tempPath))
            {
                resourceFiles.AddRange(directory.Contains(resourceDirectoryName)
                                           ? ReadDirectoryFiles(directory) // read
                                           : ReadDirectories(directory, resourceDirectoryName)); // recurse
            }

            return resourceFiles;
        }

        protected abstract IEnumerable<ResourceFile> ReadDirectoryFiles(string path); 
        
        /// <summary>
        /// Attemps to read all Excel files in a directory and convert them to <see cref="ResourceFile">ResourceFile</see>/>
        /// </summary>
        /// <param name="tempDirectory"></param>
        /// <returns></returns>
        protected virtual IEnumerable<ResourceFile> ReadExcelFiles(string tempDirectory)
        {
            var resourceFiles = Directory.GetFiles(tempDirectory)
                .Where(f => Path.GetExtension(f) == ".xls")
                .Select(file => ResourceFileReaderFactory
                    .GetReader(file, ResourceType.Excel))
                    .Select(reader => reader.Read())
                    .ToList();

            return resourceFiles;
        }

        /// <summary>
        /// Add directory structure to zip
        /// </summary>
        /// <param name="savePath">Location that zip will be saved</param>
        /// <param name="folderLocation">Path of the folder that contains the structure to be added</param>
        protected virtual void SaveZipDirectory(string savePath, string folderLocation)
        {
            using (var zip = new ZipFile())
            {
                zip.AddDirectory(folderLocation);
                zip.Save(savePath);
            }
        }

        /// <summary>
        /// Saves files to their respective resource format
        /// </summary>
        /// <param name="tempDirectory">Path to temporary directory</param>
        /// <param name="files">List of resource files</param>
        /// <returns></returns>
        protected virtual string SaveToStructure(string tempDirectory, IEnumerable<ResourceFile> files)
        {
            // create a folder in our temp dir to organize this mess
            var structuredFolder = string.Format("{0}\\{1}", tempDirectory, "Resources");
            var newStructure = Directory.CreateDirectory(structuredFolder);

            foreach (var file in files)
            {
                var saveFilePath = GetFilePathFromFileName(file.GetFileNameWithoutExtension());
                var saveDirectoryPath = string.Format("{0}\\{1}", newStructure.FullName, Path.GetDirectoryName(saveFilePath) ?? string.Empty);
                var fullSavePath = string.Format("{0}\\{1}", newStructure.FullName, saveFilePath);

                if (!Directory.Exists(saveDirectoryPath)) Directory.CreateDirectory(saveDirectoryPath);

                SaveActionInvoker.Save(file, SaveFileType, fullSavePath);
            }

            return newStructure.FullName;
        }

        /// <summary>
        /// Saves resource files to temp directory as Excel
        /// </summary>
        /// <param name="tempPath">Temp directory path</param>
        /// <param name="resourceFiles">List of resource files to write</param>
        /// <returns></returns>
        protected virtual IEnumerable<string> SaveTempExcelFiles(string tempPath, IEnumerable<ResourceFile> resourceFiles)
        {
            var tempFilePaths = new List<string>();
            foreach (var file in resourceFiles)
            {
                var newName = GetExportFileName(file);
                var newFileTempPath = string.Format("{0}\\{1}", tempPath, newName);
                SaveActionInvoker.Save(file, SaveFileType.Excel, newFileTempPath);
                tempFilePaths.Add(newFileTempPath);
            }
            return tempFilePaths;
        }

        /// <summary>
        /// Creates a working directory and extracts the contents of the incoming zip file to it
        /// </summary>
        /// <param name="inputPath">Path to the incoming zip file</param>
        /// <returns></returns>
        protected virtual DirectoryInfo ExtractZipToTempDirectory(string inputPath)
        {
            // create temp directory
            var directoryName = string.Format("{0}{1}", Path.GetTempPath(), Guid.NewGuid());
            var tempDirectory = Directory.CreateDirectory(directoryName);

            // unpack the zip to temp directory
            using (var zip = ZipFile.Read(inputPath))
            {
                foreach (var entry in zip)
                {
                    entry.Extract(tempDirectory.FullName, ExtractExistingFileAction.OverwriteSilently);
                }
            }
            return tempDirectory;
        }

        public abstract void ToExcel(string inputPath, string outputPath);
        public abstract void ToResource(string inputPath, string outputPath);
    }
}
